﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace DaBot
{
    class CSIRC
    {
        private static TcpClient IRCServer;

        private Action<Exception> HandleException;
        private Action<string, string, string, string> HandleMessage;//Channel, text, user, userhost
        private Action<string, string, string, string> HandleKick;//Channel, kicker, kicked, reason
        private Action<string, string, string> HandlePart;//Channel, user, message
        private Action<string, string> HandleJoin;//Channel,User
        private Action<string, string> HandleQuit;//Channel, user, reason
        private Action<string, string, string> HandleMode;//Channel, setby, modes
        private Action<string, string, string> HandleTopic;//chan, user, topic
        private Action<string> HandlerOverride;
        private Action<string> RawSentString;
        private Action<string> RawRecvString;
        private Action<string> UnhandledResponse;

        private static StreamReader ReadStream;
        private static StreamWriter WriteStream;
        private static Thread Listen;
        private string nick, identd, real, serverout;
        private bool Connected;
        int hostoccurance;
        public void RegisterTopicCallback(Action<string, string, string> handler)
        {
            HandleTopic = handler;
        }
        public void RegisterUnhandledResponseCallback(Action<string> handler)
        {
            UnhandledResponse = handler;
        }
        public void RegisterModeCallback(Action<string, string, string> handler)
        {
            HandleMode = handler;
        }
        public void RegisterRawRecvStringCallback(Action<string> handler)
        {
            RawRecvString = handler;
        }
        public void RegisterRawSentStringCallblack(Action<string> handler)
        {
            RawSentString = handler;
        }
        public void RegisterExceptionCallback(Action<Exception> handler)
        {
            HandleException = handler;
        }
        public void RegisterMessageCallback(Action<string, string, string, string> handler)
        {
            HandleMessage = handler;
        }
        public void RegisterKickCallback(Action<string, string, string, string> handler)
        {
            HandleKick = handler;
        }
        public void RegisterPartCallback(Action<string, string, string> handler)
        {
            HandlePart = handler;
        }
        public void RegisterJoinCallback(Action<string, string> handler)
        {
            HandleJoin = handler;
        }
        public void RegisterQuitCallback(Action<string, string> handler)
        {
            HandleQuit = handler;
        }
        public void WaitForConnected()
        {
            while (Connected == false) ;
            return;
        }


        public void Connect(string server, Int16 port, string password = null, string nickname = "CSIRC", string ident = "CSIRC", string realname = "CSIRC")
        {
            nick = nickname;
            identd = ident;
            real = realname;
            try
            {
                IRCServer = new TcpClient(server, port);
            }
            catch (Exception e)
            {
                HandleException(e);
            }
            if (IRCServer.Connected)
            {
                ReadStream = new StreamReader(IRCServer.GetStream());
                WriteStream = new StreamWriter(IRCServer.GetStream());
                Listen = new Thread(Listener);
                Listen.Start();
            }
        }

        private void Send(string line)
        {
            WriteStream.WriteLine(line + "\r\n");
            if (RawSentString != null)
            {
                RawSentString(line);
            }
            WriteStream.Flush();
        }
        private string Recv()
        {
            return ReadStream.ReadLine();
        }
        private void RegisterConnection()
        {
            Send("USER " + identd.ToLower() + " 0 * : " + real);
            Send("NICK " + nick);
        }
        public void SendMessage(string channel, string message)
        {
            Send("PRIVMSG " + channel + " :" + message);
            //HandleMessage(channel,message,nick, "myhost");
        }
        public void BanUser(string channel, string username)
        {
            banChan = channel;
            HandlerOverride = OverriddenWhois;
            this.Raw("WHOIS " + username);
        }
        private static string banChan;
        public void OverriddenWhois(string srv)
        {
            string[] servout = srv.Split(' ');
            string command = (string)servout.GetValue(1);
            if (Convert.ToInt32(command) == 311)
            {
                string hostname = (string)servout.GetValue(5);
                this.ChanMode(banChan, "+b " + hostname);
            }
            HandlerOverride = null;
        }
        public void Action(string channel, string action)
        {
            SendMessage(channel, '\0' + "01" + "ACTION " + action + '\0' + "01");
        }
        public void JoinChannel(string channel)
        {
            Send("JOIN " + channel);
            HandleJoin(channel, nick);
        }
        public void PartChannel(string channel, string message = " ")
        {
            Send("PART " + channel + " :" + message);
            HandlePart(channel, nick, message);
        }
        public void KickUser(string channel, string user, string reason)
        {
            Send("KICK " + channel + " " + user + " :" + reason);
        }
        public void QuitServer(string message = "User has left the server")
        {
            Send("QUIT :" + message);
            HandleQuit(nick, message);
        }
        public void ChanMode(string channel, string modes)
        {
            Send("MODE " + channel + " " + modes);
            HandleMode(channel, nick, modes);
        }
        public void UserMode(string modes)
        {
            Send("MODE " + nick + " " + modes);
            HandleMode(nick, nick, modes);
        }
        public void Raw(string raw)
        {
            Send(raw);
        }
        private void Listener()
        {
            while (IRCServer.Connected)
            {
                serverout = ReadStream.ReadLine();
                string[] servout = serverout.Split(' ');
                string command = (string)servout.GetValue(1);
                if (HandlerOverride != null) HandlerOverride(serverout);
                RawRecvString(serverout);
                if (serverout.Contains("hostname") && hostoccurance < 2)
                {
                    if (hostoccurance == 0)
                    {
                        hostoccurance++;
                    }
                    else
                    {
                        RegisterConnection();
                    }
                    continue;
                }
                if (serverout.Contains("PING"))
                {
                    if (Connected == false)
                    {
                        Connected = true;
                    }
                    string[] local = serverout.Split(':');
                    Send("PONG :" + (string)local.GetValue(1));
                    continue;
                }
                if (serverout.Contains("END OF"))
                {
                    Connected = true;
                    continue;
                }
                if (command == "PRIVMSG")
                {
                    if (HandleMessage != null)
                    {
                        string[] local = serverout.Split(' ');
                        string nick = (string)local.GetValue(0);
                        string[] nicks = nick.Split('!');//split nick and hostmask
                        nick = (string)nicks.GetValue(0);
                        nick = nick.Remove(0, 1);//grab nick
                        string hostname = (string)nicks.GetValue(1);
                        string[] stz = hostname.Split(' ');
                        hostname = (string)stz.GetValue(0);
                        string chan = (string)local.GetValue(2);
                        //:Ox90!Brandon@Network.Admin.KaosNetworks.Com PRIVMSG #sourced :wat
                        local = serverout.Split(' ');
                        int len = ((string)local.GetValue(0)).Length + ((string)local.GetValue(1)).Length + ((string)local.GetValue(2)).Length + 4;
                        string message = serverout.Remove(0, len);
                        HandleMessage(chan, message, nick, hostname);
                        continue;
                    }
                }
                if (command == "KICK")
                {
                    if (HandleKick != null)
                    {
                        string[] local = serverout.Split(' ');
                        string kicker = (string)local.GetValue(0);
                        string[] kickers = kicker.Split('!');
                        kicker = (string)kickers.GetValue(0);
                        kicker = kicker.Remove(0, 1);
                        string channel = (string)local.GetValue(2);
                        string kicked = (string)local.GetValue(3);
                        local = serverout.Split(':');
                        string reason = (string)local.GetValue(2);
                        HandleKick(channel, kicker, kicked, reason);
                        continue;
                    }
                }
                if (command == "JOIN")
                {
                    if (HandleJoin != null)
                    {
                        string[] local = serverout.Split(' ');
                        string channel = (string)local.GetValue(2);
                        channel = channel.Remove(0, 1);
                        string nick = (string)local.GetValue(0);
                        local = nick.Split('!');
                        nick = (string)local.GetValue(0);
                        nick = nick.Remove(0, 1);
                        HandleJoin(channel, nick);
                        continue;
                    }
                }
                if (command == "PART")
                {
                    if (HandlePart != null)
                    {
                        string[] local = serverout.Split(' ');
                        string channel = (string)local.GetValue(2);
                        string nick = (string)local.GetValue(0);
                        string[] nicks = nick.Split('!');
                        nick = (string)nicks.GetValue(0);
                        nick = nick.Remove(0, 1);
                        local = serverout.Split(':');
                        string message = (string)local.GetValue(2);
                        HandlePart(channel, nick, message);
                        continue;
                    }
                }
                if (command == "QUIT")
                {
                    if (HandleQuit != null)
                    {
                        string[] local = serverout.Split('!');
                        string user = (string)local.GetValue(0);
                        user.Remove(0, 1);
                        local = serverout.Split(':');
                        string message = (string)local.GetValue(1);
                        HandleQuit(user, message);
                        continue;
                    }
                
                }
                if (command == "MODE")
                {
                    if (HandleMode != null)
                    {
                        string[] local = serverout.Split('!');
                        string nick = (string)local.GetValue(0);
                        nick = nick.Remove(0, 1);
                        local = serverout.Split(' ');
                        string chan = (string)local.GetValue(2);
                        string modes = "0";
                        if (serverout.Contains('-'))
                        {
                            local = serverout.Split('-');
                            modes = "-";
                        }
                        if (serverout.Contains('+'))
                        {
                            modes = "+";
                            local = serverout.Split('+');
                        }
                        modes += (string)local.GetValue(1);
                        HandleMode(chan, nick, modes);
                    }
                }
                if(command == "TOPIC")
                {
                    if (HandleTopic != null)
                    {
                        string[] local = serverout.Split('!');
                        string nick = (string)local.GetValue(0);
                        nick = nick.Remove(0, 1);
                        local = serverout.Split(' ');
                        string chan = (string)local.GetValue(2);
                        local = serverout.Split(':');
                        string topic = (string)local.GetValue(1);
                        HandleTopic(chan, nick, topic);
                    }
                }
                UnhandledResponse(serverout);
            }
        }
    }
}

